﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;

using Storm.TextEditor.Core;
using Storm.TextEditor.Win32;
using Storm.TextEditor.Win32.Enums;

namespace Storm.TextEditor.Drawing
{
	/// <summary>
	/// Represents a sealed class holding several methods for drawing.
	/// </summary>
	public sealed class DrawingTools
	{
		#region Methods

		#region Public

		/// <summary>
		/// Resets the given Graphics variable.
		/// </summary>
		/// <param name="g">Graphics to reset.</param>
		public static void ResetGraphics(Graphics g)
		{
			g.ReleaseHdc(g.GetHdc());
		}

		/// <summary>
		/// Mixes the given colors and returns the new color.
		/// </summary>
		/// <param name="c1">First color to mix.</param>
		/// <param name="c2">Second color to mix.</param>
		/// <param name="mix">Factor of the mixing.</param>
		/// <returns>The mixed color.</returns>
		public static Color MixColors(Color c1, Color c2, double mix)
		{
			return Color.FromArgb((int)(c1.R * (1 - mix) + c2.R * mix), (int)(c1.G * 
				(1 - mix) + c2.G * mix), (int)(c1.B * (1 - mix) + c2.B * mix));
		}

		/// <summary>
		/// Draws a border depending on the given ControlBorderStyle with specific color at the given Rectangle.
		/// </summary>
		/// <param name="style">ControlBorderStyle that holds how the method should draw the border.</param>
		/// <param name="borderColor">Color of the drawn border.</param>
		/// <param name="g">Graphics to use when drawing.</param>
		/// <param name="r">Rectangle to draw the border at.</param>
		public static void DrawBorder(ControlBorderStyle style, Color borderColor, Graphics g, Rectangle r)
		{
			switch (style)
			{
				case ControlBorderStyle.Dotted:
					{
						r.Width--;
						r.Height--;

						g.DrawRectangle(new Pen(SystemColors.Control), r);
						Pen p = new Pen(borderColor);
						p.DashStyle = DashStyle.Dot;
						g.DrawRectangle(p, r);

						break;
					}
				case ControlBorderStyle.Dashed:
					{
						r.Width--;
						r.Height--;

						g.DrawRectangle(new Pen(SystemColors.Control), r);
						Pen p = new Pen(borderColor);
						p.DashStyle = DashStyle.Dash;
						g.DrawRectangle(p, r);

						break;
					}
				case ControlBorderStyle.Sunken:
					{
						if (borderColor == Color.Black)
							borderColor = SystemColors.Control;

						DrawingTools.DrawBorder(Border3DStyle.Sunken, borderColor, g, r);

						break;
					}
				case ControlBorderStyle.FixedSingle:
					{
						r.Width--;
						r.Height--;

						g.DrawRectangle(new Pen(borderColor), r);
						break;
					}
				case ControlBorderStyle.FixedDouble:
					{
						g.DrawRectangle(new Pen(borderColor), r.Left, r.Top, r.Width - 1, r.Height - 1);
						g.DrawRectangle(new Pen(borderColor), r.Left + 1, r.Top + 1, r.Width - 3, r.Height - 3);

						break;
					}
				case ControlBorderStyle.Raised:
					{
						if (borderColor == Color.Black)
							borderColor = SystemColors.Control;

						DrawingTools.DrawBorder(Border3DStyle.Raised, borderColor, g, r);
						break;
					}
				case ControlBorderStyle.RaisedThin:
					{
						if (borderColor == Color.Black)
						{
							borderColor = Color.FromArgb(SystemColors.Control.R,
								SystemColors.Control.G, SystemColors.Control.B);
						}

						DrawingTools.DrawBorder(Border3DStyle.RaisedInner,
							borderColor, g, r);

						break;
					}
				case ControlBorderStyle.SunkenThin:
					{
						if (borderColor == Color.Black)
						{
							borderColor = Color.FromArgb(SystemColors.Control.R,
								SystemColors.Control.G, SystemColors.Control.B);
						}

						DrawingTools.DrawBorder(Border3DStyle.SunkenOuter,
							borderColor, g, r);
						break;
					}
				case ControlBorderStyle.Etched:
					{
						if (borderColor == Color.Black)
							borderColor = SystemColors.Control;

						ControlPaint.DrawBorder3D(g, r,
							Border3DStyle.Etched);

						break;
					}
				case ControlBorderStyle.Bump:
					{
						if (borderColor == Color.Black)
							borderColor = SystemColors.Control;

						SolidBrush b = new SolidBrush(borderColor);
						g.FillRectangle(b, r.Left, r.Top, r.Width, 4);
						g.FillRectangle(b, r.Left, r.Bottom - 4, r.Width, 4);

						g.FillRectangle(b, r.Left, r.Top, 4, r.Height);
						g.FillRectangle(b, r.Right - 4, r.Top, 4, r.Height);
						b.Dispose();

						DrawingTools.DrawBorder(Border3DStyle.Raised, borderColor, g, r);
						DrawingTools.DrawBorder(Border3DStyle.Sunken, borderColor, g,
							new Rectangle(r.Left + 4, r.Top + 4, r.Width - 8,
							r.Height - 8));

						break;
					}
				case ControlBorderStyle.Column:
					{
						SolidBrush normal = new SolidBrush(borderColor);
						SolidBrush light = new SolidBrush(MixColors(borderColor,
							Color.White, 1));

						SolidBrush dark = new SolidBrush(MixColors(borderColor,
							Color.Black, 0.4));

						SolidBrush darkdark = new SolidBrush(Color.FromArgb
							(borderColor.A, System.Windows.Forms.ControlPaint.
							DarkDark(borderColor)));

						g.FillRectangle(light, r.Left, r.Top, r.Width, 1);
						g.FillRectangle(light, r.Left, r.Top + 3, 1, r.Height - 1 - 6);
						g.FillRectangle(dark, r.Right - 1, r.Top + 3, 1, r.Height - 6);
						g.FillRectangle(dark, r.Left, r.Bottom - 1, r.Width, 1);

						break;
					}
				case ControlBorderStyle.Row:
					{
						SolidBrush normal = new SolidBrush(borderColor);
						SolidBrush light = new SolidBrush(MixColors(borderColor,
							Color.White, 1));

						SolidBrush dark = new SolidBrush(MixColors(borderColor,
							Color.Black, 0.4));

						SolidBrush darkdark = new SolidBrush(Color.FromArgb
							(borderColor.A, System.Windows.Forms.ControlPaint.DarkDark
							(borderColor)));

						g.FillRectangle(light, r.Left + 3, r.Top, r.Width - 6, 1);
						g.FillRectangle(light, r.Left, r.Top, 1, r.Height - 1);
						g.FillRectangle(dark, r.Right - 1, r.Top, 1, r.Height);
						g.FillRectangle(dark, r.Left + 3, r.Bottom - 1, r.Width - 6, 1);

						break;
					}
			}
		}

		/// <summary>
		/// Draws a line at the given coordinates.
		/// </summary>
		/// <param name="g">Graphics to use when drawing.</param>
		/// <param name="x1">Start x coordinate.</param>
		/// <param name="y1">Start y coordinate.</param>
		/// <param name="x2">End x coordinate.</param>
		/// <param name="y2">End y coordinate.</param>
		public static void DrawDesignTimeLine(Graphics g, int x1, int y1, int x2, int y2)
		{
			Pen p = new Pen(SystemColors.ControlDarkDark);

			p.DashOffset = 10;
			p.DashStyle = DashStyle.Dash;

			g.DrawLine(p, x1, y1, x2, y2);
			p.Dispose();
		}

		/// <summary>
		/// Draws the given image as a gray version.
		/// </summary>
		/// <param name="g">Graphics to use when drawing.</param>
		/// <param name="image">Image to draw.</param>
		/// <param name="x">X coordinate to draw the image at.</param>
		/// <param name="y">Y coordinate to draw the image at.</param>
		/// <param name="transparencyFactor">Factor of the transparency.</param>
		public static void DrawGrayImage(Graphics g, Image image, int x, int y, float transparencyFactor)
		{
			ColorMatrix cm = new ColorMatrix();
			ImageAttributes ia = new ImageAttributes();

			cm.Matrix33 = transparencyFactor;
			cm.Matrix00 = 0.33333334F;
			cm.Matrix01 = 0.33333334F;
			cm.Matrix02 = 0.33333334F;
			cm.Matrix10 = 0.33333334F;
			cm.Matrix11 = 0.33333334F;
			cm.Matrix12 = 0.33333334F;
			cm.Matrix20 = 0.33333334F;
			cm.Matrix21 = 0.33333334F;
			cm.Matrix22 = 0.33333334F;

			ia.SetColorMatrix(cm);
			g.DrawImage(image, new Rectangle(x, y, image.Width, image.Height), 0, 0,
				image.Width, image.Height, GraphicsUnit.Pixel, ia);
		}

		/// <summary>
		/// Draws the given image as a transparent version.
		/// </summary>
		/// <param name="g">Graphics to use when drawing.</param>
		/// <param name="image">Image to draw.</param>
		/// <param name="x">X coordinate to draw the image at.</param>
		/// <param name="y">Y coordinate to draw the image at.</param>
		/// <param name="transparencyFactor">Factor of the transparency.</param>
		public static void DrawTransparentImage(Graphics g, Image image, int x, int y, float transparencyFactor)
		{
			ImageAttributes ia = new ImageAttributes();
			ColorMatrix cm = new ColorMatrix();

			cm.Matrix33 = transparencyFactor;
			cm.Matrix00 = 1.0F;
			cm.Matrix11 = 1.0F;
			cm.Matrix22 = 1.0F;

			ia.SetColorMatrix(cm);
			g.DrawImage(image, new Rectangle(x, y, image.Width, image.Height), 0, 0,
				image.Width, image.Height, GraphicsUnit.Pixel, ia);
		}

		/// <summary>
		/// Draws a border using DashStyle.Dash at the given Rectangle.
		/// </summary>
		/// <param name="g">Graphics to use when drawing.</param>
		/// <param name="rect">Rectangle to draw the border at.</param>
		public static void DrawDesignTimeBorder(Graphics g, Rectangle rect)
		{
			rect.Width--;
			rect.Height--;

			Pen p = new Pen(SystemColors.ControlDarkDark);
			p.DashStyle = DashStyle.Dash;

			g.DrawRectangle(p, rect);
			p.Dispose();
		}

		public static void DrawInsertIndicatorH(int x, int y, int width, Graphics g, Color c)
		{
			y -= 3;
			x -= 2;

			ControlPaint.FillReversibleRectangle(new Rectangle(x, y, 2, 7), c);
			ControlPaint.FillReversibleRectangle(new Rectangle(x + 2, y + 1, width, 5), c);
			ControlPaint.FillReversibleRectangle(new Rectangle(width + 2 + x, y, 2, 7), c);
		}

		public static void DrawInsertIndicatorV(int x, int y, int height, Graphics g, Color c)
		{
			x -= 3;
			y -= 2;

			ControlPaint.FillReversibleRectangle(new Rectangle(x, y, 7, 2), c);
			ControlPaint.FillReversibleRectangle(new Rectangle(x + 1, y + 2, 5, height), c);
			ControlPaint.FillReversibleRectangle(new Rectangle(x, height + 2 + y, 7, 2), c);
		}

		/// <summary>
		/// Draws the given Control.
		/// </summary>
		/// <param name="control">Control to draw.</param>
		/// <returns>Bitmap created from the given Control.</returns>
		public static Bitmap DrawControl(Control control)
		{
			Bitmap b = new Bitmap(control.Width, control.Height);

			Graphics g = Graphics.FromImage(b);
			IntPtr hdc = g.GetHdc();

			int i = NativeUser32Api.SendMessage(control.Handle,
				(int)WindowMessage.WM_PRINT, (int)hdc,
				(int)(WMPrintFlags.PRF_CLIENT |
				WMPrintFlags.PRF_ERASEBKGND));

			g.ReleaseHdc(hdc);
			g.Dispose();

			return b;
		}

		/// <summary>
		/// Draws the given Control.
		/// </summary>
		/// <param name="control">Control to draw.</param>
		/// <param name="b">Bitmap to create graphics from.</param>
		/// <returns>Whether the drawing was succesful.</returns>
		public static bool DrawControl(Control control, Bitmap b)
		{
			Graphics g = Graphics.FromImage(b);
			IntPtr hdc = g.GetHdc();

			int result = NativeUser32Api.SendMessage(control.Handle, (int)WindowMessage.WM_PRINT, (int)hdc, 
				(int)(WMPrintFlags.PRF_CLIENT | WMPrintFlags.PRF_ERASEBKGND));
			
			g.ReleaseHdc(hdc);
			g.Dispose();
			
			return result != 0;
		}

		public static void DrawSortArrow(int x, int y, Graphics g, bool ascending)
		{
			Color c1 = Color.FromArgb(220, 255, 255, 255);
			Color c2 = Color.FromArgb(140, 0, 0, 0);

			Pen p1 = new Pen(c1);
			Pen p2 = new Pen(c2);

			if (ascending == true)
			{
				g.DrawLine(p1, x, y + 6, x + 7, y + 6);
				g.DrawLine(p2, x + 3, y, x, y + 6);
				g.DrawLine(p1, x + 4, y, x + 7, y + 6);
			}
			else
			{
				g.DrawLine(p2, x, y, x + 7, y);
				g.DrawLine(p2, x, y, x + 3, y + 6);
				g.DrawLine(p1, x + 7, y, x + 4, y + 6);
			}
		}

		#endregion

		#region Private

		private static void DrawBorder(Border3DStyle Style, Color BorderColor, Graphics g, Rectangle r)
		{
			SolidBrush normal = new SolidBrush(BorderColor);
			SolidBrush light;

			if (BorderColor.GetBrightness() > 0.6)
				light = new SolidBrush(MixColors(BorderColor, Color.White, 1));
			else
				light = new SolidBrush(MixColors(BorderColor, Color.White, 0.5));

			SolidBrush dark;
			SolidBrush darkdark;

			if (BorderColor.GetBrightness() < 0.5)
			{
				dark = new SolidBrush(MixColors(BorderColor, Color.Black, 0.7));
				darkdark = new SolidBrush(MixColors(BorderColor, Color.Black, 1));
			}
			else
			{
				dark = new SolidBrush(MixColors(BorderColor, Color.Black, 0.4));
				darkdark = new SolidBrush(MixColors(BorderColor, Color.Black, 0.6));
			}

			switch (Style)
			{
				case Border3DStyle.Sunken:
					{
						g.FillRectangle(dark, r.Left, r.Top, r.Width, 1);
						g.FillRectangle(dark, r.Left, r.Top, 1, r.Height);
						g.FillRectangle(darkdark, r.Left + 1, r.Top + 1, r.Width - 2, 1);
						g.FillRectangle(darkdark, r.Left + 1, r.Top + 1, 1, r.Height - 2);

						g.FillRectangle(light, r.Right - 1, r.Top + 1, 1, r.Height - 1);
						g.FillRectangle(light, r.Left + 1, r.Bottom - 1, r.Width - 1, 1);
						g.FillRectangle(normal, r.Right - 2, r.Top + 2, 1, r.Height - 3);
						g.FillRectangle(normal, r.Left + 2, r.Bottom - 2, r.Width - 3, 1);
						break;
					}
				case Border3DStyle.Raised:
					{
						g.FillRectangle(normal, r.Left, r.Top, r.Width - 1, 1);
						g.FillRectangle(normal, r.Left, r.Top, 1, r.Height - 1);
						g.FillRectangle(light, r.Left + 1, r.Top + 1, r.Width - 2, 1);
						g.FillRectangle(light, r.Left + 1, r.Top + 1, 1, r.Height - 2);

						g.FillRectangle(darkdark, r.Right - 1, r.Top, 1, r.Height);
						g.FillRectangle(darkdark, r.Left, r.Bottom - 1, r.Width, 1);
						g.FillRectangle(dark, r.Right - 2, r.Top + 1, 1, r.Height - 2);
						g.FillRectangle(dark, r.Left + 1, r.Bottom - 2, r.Width - 2, 1);

						break;
					}
				case Border3DStyle.RaisedInner:
					{
						g.FillRectangle(light, r.Left, r.Top, r.Width - 1, 1);
						g.FillRectangle(light, r.Left, r.Top, 1, r.Height - 1);

						g.FillRectangle(dark, r.Right - 1, r.Top, 1, r.Height);
						g.FillRectangle(dark, r.Left, r.Bottom - 1, r.Width, 1);

						break;
					}
				case Border3DStyle.SunkenOuter:
					{
						g.FillRectangle(dark, r.Left, r.Top, r.Width, 1);
						g.FillRectangle(dark, r.Left, r.Top, 1, r.Height);

						g.FillRectangle(light, r.Right - 1, r.Top + 1, 1, r.Height - 1);
						g.FillRectangle(light, r.Left + 1, r.Bottom - 1, r.Width - 1, 1);

						break;
					}
				case Border3DStyle.Etched:
					break;
				default:
					break;
			}

			normal.Dispose();
			light.Dispose();
			dark.Dispose();
			darkdark.Dispose();
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of DrawingTools.
		/// </summary>
		private DrawingTools()
		{
		}
	}
}
